/*

 The circuit:
  * CS - to digital pin 10  (SS pin)
  * SDI - to digital pin 11 (MOSI pin)
  * SDO - to digital pin 12 (MISO pin)
  * CLK - to digital pin 13 (SCK pin)
 
*/


// inslude the SPI library:
#include <SPI.h>

#define BLOCK_ERASE	0xd8
#define GET_FEATURE	0x0f
  #define FEATURE_BLOCK_LOCK  0xA0
  #define FEATURE_OTP         0xB0
  #define FEATURE_STATUS      0xC0
#define PAGE_READ	0x13
#define PROGRAM_EXECUTE	0x10
#define PROGRAM_LOAD	0x02
#define PROGRAM_LOAD_RANDOM_DATA 0x84
#define READ_FROM_CACHE	  0x03
#define READ_FROM_CACHE_2 0x3b
#define READ_FROM_CACHE_4 0x6b
#define READ_ID	0x9f
#define RESET	0xff
#define SET_FEATURE	0x1f
#define WRITE_DISABLE	0x04
#define WRITE_ENABLE	0x06
#define DUMMY_BYTE      42

const int slaveSelectPin = 8;
const int writeProtectPin = 9;
const int holdPin = 10;
const int mosiPin = 11;
const int misoPin = 12;
const int sckPin = 13;

void setup() 
{
  // set the slaveSelectPin as an output:
  /*
  pinMode(slaveSelectPin, OUTPUT);
  digitalWrite(slaveSelectPin, HIGH);
  pinMode(writeProtectPin, OUTPUT);
  digitalWrite(writeProtectPin, HIGH);
  pinMode(holdPin, OUTPUT);
  digitalWrite(holdPin, HIGH);
  pinMode(mosiPin, OUTPUT);
  pinMode(misoPin, INPUT);
  pinMode(sckPin, OUTPUT);
  
  pinMode(6, OUTPUT);
  digitalWrite(6, LOW);
  */
  
  // initialize SPI:
  SPI.begin(); 
  
  SPI.setDataMode(SPI_MODE0);
  
  //SPCR |= 0x04;
  Serial.begin(19200);
  pinMode(slaveSelectPin, OUTPUT);
  digitalWrite(slaveSelectPin, HIGH);
  digitalWrite(holdPin, HIGH);
  digitalWrite(writeProtectPin, LOW);
}

void loop() 
{  
  delay(1000);
  //reset();
  Serial.print("id: ");
  Serial.println(readID());
  writeEnable();
  programLoad(0, 0);
  readFromCache(0, 0);
}


void writeEnable()
{
  digitalWrite(slaveSelectPin, LOW);
  SPI.transfer(WRITE_ENABLE);
  digitalWrite(slaveSelectPin, HIGH);
}

void blockErase(int address)
{
  writeEnable();
  digitalWrite(slaveSelectPin, LOW);
  SPI.transfer(BLOCK_ERASE);
  SPI.transfer(0);
  SPI.transfer(0);
  SPI.transfer(0);
  digitalWrite(slaveSelectPin, HIGH);
}

void programExecute(uint32_t address)
{
  digitalWrite(slaveSelectPin, LOW);
  SPI.transfer(PROGRAM_EXECUTE);
  SPI.transfer((address >> 16) & 0xFF);
  SPI.transfer((address >> 8) & 0xFF);
  SPI.transfer(address & 0xFF);
  digitalWrite(slaveSelectPin, HIGH);
}

void programLoad(int plane, uint16_t column)
{
  uint16_t address = ((plane & 0x01) << 12) | (column & 0x0FFF);
  digitalWrite(slaveSelectPin, LOW);
  SPI.transfer(PROGRAM_LOAD);
  SPI.transfer((address >> 8) & 0xFF); // Shift out 8 high bits
  SPI.transfer(address && 0xFF); // Shift out 8 low bits
  for (int i = 0; i < 2112; i++)
  {
    SPI.transfer(i % 0xFF);
  }
  digitalWrite(slaveSelectPin, HIGH);
}

int readFromCache(int plane, uint16_t column)
{
  uint16_t address = ((plane & 0x01) << 12) | (column & 0x0FFF);
    
  digitalWrite(slaveSelectPin, LOW);
  SPI.transfer(READ_FROM_CACHE);
  SPI.transfer((address >> 8) & 0xFF); // Shift out 8 high bits
  SPI.transfer(address && 0xFF); // Shift out 8 low bits
  SPI.transfer(DUMMY_BYTE);
  for (int i = 0; i < 44; i++)
  {
    for (int j = 0; j < 48; j++)
    {
      Serial.print(SPI.transfer(DUMMY_BYTE));
      Serial.print(" ");
    }
    Serial.println();
  }
  digitalWrite(slaveSelectPin, HIGH);
}

void reset()
{
  digitalWrite(slaveSelectPin, LOW);
  SPI.transfer(RESET);
  digitalWrite(slaveSelectPin, HIGH);
}

int operationInProgress()
{
  return getFeature(FEATURE_STATUS) & 0x01;
}

void pageRead(uint32_t address)
{
  digitalWrite(slaveSelectPin, LOW);
  SPI.transfer(PAGE_READ);
  SPI.transfer((address >> 16) & (0x03)); // Only the most significant 2 bits are valid.
  SPI.transfer((address >> 8) & (0xFF)); 
  SPI.transfer((address) & (0xFF));
  digitalWrite(slaveSelectPin, HIGH);
}


int getFeature(int address)
{
  digitalWrite(slaveSelectPin, LOW);
  SPI.transfer(GET_FEATURE);
  SPI.transfer(address);
  int feature = SPI.transfer(DUMMY_BYTE);
  digitalWrite(slaveSelectPin, HIGH);
  return feature;
}

uint16_t readID()
{
  digitalWrite(slaveSelectPin, LOW);
  SPI.transfer(READ_ID);
  SPI.transfer(DUMMY_BYTE);
  uint16_t id = SPI.transfer(DUMMY_BYTE) << 8;
  id |= SPI.transfer(DUMMY_BYTE);
  digitalWrite(slaveSelectPin, HIGH);
  return id;
}
